/* Scheduler include files. */
#include <string.h>
#include <stdlib.h>
#include "FreeRTOS.h"
#include "task.h"
#include "uart.h"
#include "lcd.h"
#include "constants.h"
#include "game.h"

/* Demo program include files. */
#include "serial.h"
#include "semphr.h" // semaphores
#include "TI_CC2500.h"


#define comSTACK_SIZE				configMINIMAL_STACK_SIZE
#define comTX_LED_OFFSET			( 0 )
#define comRX_LED_OFFSET			( 1 )
#define comTOTAL_PERMISSIBLE_ERRORS ( 2 )

/* The Tx task will transmit the sequence of characters at a pseudo random
interval.  This is the maximum and minimum block time between sends. */
#define comHEART_WAIT			( ( portTickType ) 10000/portTICK_RATE_MS )
#define comLISTEN_WAIT			( ( portTickType ) 2000/portTICK_RATE_MS )
#define comHEART_SEND			( ( portTickType ) 2000/portTICK_RATE_MS )
#define comSEND_DELAY			( ( portTickType ) 250/portTICK_RATE_MS )
#define comWAIT_TIME			( ( portTickType ) 500/portTICK_RATE_MS )
#define NO_BLOCK_TIME			( ( portTickType ) 0 )

/* The Heartbeat transmit task. */
//static portTASK_FUNCTION_PROTO( vHeartBeat, pvParameters );

/* The Heartbeat receive task. */
static portTASK_FUNCTION_PROTO( vPreGameListen, pvParameters );

/* The guess(ours) and reply(to them) transmit task. */
//static portTASK_FUNCTION_PROTO( vSendGameData, pvParameters );

/* The guess(theirs) and reply(from them) receive task. */
static portTASK_FUNCTION_PROTO( vListenGameData, pvParameters );

/* The transmit task blocks on a queue until something is placed there to be sent. */
static portTASK_FUNCTION_PROTO( vTransmitAll, pvParameters );

/* The LED that should be toggled by the Rx and Tx tasks.  The Rx task will
toggle LED ( uxBaseLED + comRX_LED_OFFSET).  The Tx task will toggle LED
( uxBaseLED + comTX_LED_OFFSET ). */
static unsigned portBASE_TYPE uxBaseLED = 0;

/* Tasks */
static xTaskHandle heartBeat = NULL;
static xTaskHandle transmitALL = NULL;
static xTaskHandle preGame = NULL;
//static xTaskHandle guessAndReplyTx = NULL;
static xTaskHandle replyAndGuessRx = NULL;

static xComPortHandle xPort = NULL;
static portBASE_TYPE xErrorOccurred = pdFALSE;
extern xSemaphoreHandle xPrintSemaphore;
extern xSemaphoreHandle xReplySemaphore;  // for guess/reply synchronisation
extern xSemaphoreHandle xEndGameSemaphore;  // for end game synchronisation
extern xSemaphoreHandle xStartSemaphore;
extern PACKET lastReceived;
extern state;
extern char rxBuffer[64];
extern char txBuffer[64];
extern int guessCount;


static xSemaphoreHandle xTxSemaphore;
static portTickType xHeartTime;

//static portBASE_TYPE xHBReceived = pdFALSE;
int beatCount = 0;
int beatSuccess = 0;
int currentMode = 1;
int gameStat = 0;

/*-----------------------------------------------------------*/
void vStartHeartTasks( unsigned portBASE_TYPE uxPriority, unsigned portBASE_TYPE uxLED ) // start listening for game packets (ie. guess, reply, heartbeat, and server msgs)
{
	/* Initialise the com port then spawn the Rx and Tx tasks. */
	uxBaseLED = uxLED;

        // Mutexes to protect transfer and receive
        xTxSemaphore = xSemaphoreCreateMutex();

	//xTaskCreate( vHeartBeat, ( signed char * ) "HBeat", comSTACK_SIZE, NULL, uxPriority, &heartBeat ); // heart beat listening task
	//txTaskCreate( vHeartListen, ( signed char * ) "HList", comSTACK_SIZE, NULL, uxPriority, &heartListen );
        xTaskCreate( vListenGameData, ( signed char * ) "GameList", comSTACK_SIZE+30, NULL, uxPriority, &replyAndGuessRx );
        
}

void vStartGuessTasks( unsigned portBASE_TYPE uxPriority ) // start transmit to allow sending
{
	xTaskCreate( vTransmitAll, ( signed char * ) "Tmit", comSTACK_SIZE+10, NULL, uxPriority, &transmitALL );
}

void vStartPreGame( unsigned portBASE_TYPE uxPriority ) // listen for incoming pre-game packets (ie. Host or Client)
{
	//xTaskCreate( vSendGameData, ( signed char * ) "HBeat", comSTACK_SIZE, NULL, uxPriority, &guessAndReplyTx );
	xTaskCreate( vPreGameListen, ( signed char * ) "PreGame", comSTACK_SIZE, NULL, uxPriority, &preGame );
}

void vGoPreGame( int option ){
  vTaskResume(preGame);
  vTaskResume(transmitALL);  // stop sending heartbeats
  gameStat = option;
}

void vPlayGame(void){
  vTaskResume(replyAndGuessRx);
}

void vEndPreGame( void ){
  vTaskSuspend(preGame);
}

void xEndAllHeart( void ){
  vTaskSuspend(transmitALL);  // stop sending heartbeats
  vTaskSuspend(replyAndGuessRx);  // stop sending heartbeats
  currentMode = 0;
}
/*-----------------------------------------------------------*/

/*static portTASK_FUNCTION( vHeartBeat, pvParameters )
{
  portTickType xTimeToWait = comHEART_SEND;
  portBASE_TYPE xSkippedBeat = pdFALSE;
  portBASE_TYPE xRetrySend = pdTRUE;
  int max_tries = 3, i;
  PACKET heart;
  beatCount =0;
  
  xHeartTime = xTaskGetTickCount();
  
	
	( void ) pvParameters;
                
	for( ;; )
	{
                vTaskDelay(xTimeToWait);
                if( xSemaphoreTake( xTxSemaphore, portMAX_DELAY ) == pdTRUE ) //place reply on the queue
                { 
                  createHeartbeatPacket(&heart, (char)beatCount);
               
                  xRetrySend = xSerialSendPkt( xPort, &heart, xTimeToWait, 1 );
                  if(xRetrySend){
                    //putChar('A');
                  }
                  xSemaphoreGive( xTxSemaphore );
                }
                
                
                if((xTaskGetTickCount() - xHeartTime) > comHEART_WAIT){
                  xSemaphoreGive( xEndGameSemaphore );
                  vTaskSuspend(NULL);
                }

	}
}
/*-----------------------------------------------------------*/

static portTASK_FUNCTION( vPreGameListen, pvParameters )
{
    int i, bulls, cows, length;
    char cExpectedSQN; // for GBN
    char xRxedData[64];
    PACKET pkt;
    portTickType xTimeToWait = comWAIT_TIME;
    portTickType xStartTime = xTaskGetTickCount();

	/* Just to stop compiler warnings. */
	(void)pvParameters;

	for( ;; )
	{
          vTaskDelay(comWAIT_TIME*2);
          
          if(xSerialGetDataPkt( xPort, xRxedData, portMAX_DELAY ) == pdTRUE){
            //length = xRxedData[0];
            pkt = receivePacket(xRxedData+1, xRxedData[0]);
            if((pkt.type == TYPE_HOSTGAME)&&gameStat==0){  // if a host
              // use semaphore synch to signal waiting game fsm that host found
              lastReceived = pkt;
              xSemaphoreGive( xStartSemaphore );
              vTaskSuspend(NULL);
            } else if((pkt.type == TYPE_JOINGAME)&&gameStat==1){  // if a client
              // use semaphore synch to signal waiting game fsm that client found
              lastReceived = pkt;
              xSemaphoreGive( xStartSemaphore );
              vTaskSuspend(NULL);
            }

          }
	}
}
/*-----------------------------------------------------------*/

/*-----------------------------------------------------------

int xSendGuess( char guessValue[4] ) // function to send game data
{
  int xErrorFree = 1;
  PACKET *guessPkt;

  if( xSemaphoreTake( xTxSemaphore, portMAX_DELAY ) == pdTRUE ) //place reply on the queue
  { 
     createGuessPacket( guessPkt, guessCount, guessValue);
     xSendGameData( guessPkt );
     guessCount++;
     xSemaphoreGive( xTxSemaphore );
  } else {
    xErrorFree = -1;
  }
  return xErrorFree;
}

portBASE_TYPE xSendGameData( PACKET *gameData ) // function to send game data
{
  portTickType xTimeToWait = comWAIT_TIME;
  portBASE_TYPE xErrorFree = pdTRUE;
  portBASE_TYPE xRetrySend = pdTRUE;
  int max_tries = 3;
  //int size = strlen(gameData);
  
  //while((xRetrySend == pdTRUE) && (max_tries > 0)){
    if(xSerialSendPkt( xPort, gameData, portMAX_DELAY, 1 ) == pdTRUE){
      xRetrySend == pdFALSE;
    }// else {
      //max_tries--;
    //}
  //}
  if(max_tries <= 0){
    xErrorFree = pdFALSE;
  }

  return xErrorFree;
}
-----------------------------------------------------------*/

void handleRx(char *xRxedData){
  char guess[4];
  char bullCow[2];
  int i, length;
  PACKET packet;
  switch(xRxedData[3]){
    case 'Z':
      beatCount = 0;
      //println("heart");
      break;
    case 'G':
      memcpy(guess, xRxedData+4, 4);
      getBullsAndCows(guess, bullCow);
      createReplyPacket(&packet, bullCow[0], bullCow[1]);
      if( xSemaphoreTake( xTxSemaphore, portMAX_DELAY ) == pdTRUE ) //place reply on the queue
      { 
        xSerialSendPkt( xPort, &packet, comSEND_DELAY*2, 1 );
        if(bullCow[0] == '4'){
          //end game
          println("YOU LOSE");
          vEndGameNow();
          //state = 6;
          //vResumeMain();
        }
        xSemaphoreGive( xTxSemaphore );
      }
      break;
    case 'Y':
      print("Bulls: ");
      print(&xRxedData[4]);
      print(" Cows: ");
      print(&xRxedData[5]);
      sendData('B');
      sendData(xRxedData[4]);
      sendData('C');
      sendData(xRxedData[5]);
      if(xRxedData[4] == '4'){
        //end game
        vEndGameNow();
        //state = 6;
        //vResumeMain();
        println("YOU WIN");
      } else {
        xSemaphoreGive( xStartSemaphore );
      }
      break;
    case 'S':
      //println(xRxedData);
      length = xRxedData[0];
      println("");
      for(i=5; i<length; i++){
        putChar(xRxedData[i]);
      }
      if(xRxedData[4] == 'T'){
        // end game
        vEndGameNow();
        //state = 6;
        //vResumeMain();
      }
      break;
  }
}


static portTASK_FUNCTION( vListenGameData, pvParameters ) // listen for replies and guesses
{
    char xRxedData[35];
    portTickType xTimeToWait = comWAIT_TIME;

	/* Just to stop compiler warnings. */
     (void)pvParameters;

      for( ;; )
      {
          vTaskDelay(comWAIT_TIME);
            
            if(xSerialGetDataPkt( xPort, xRxedData, portMAX_DELAY /*comWAIT_TIME*/ ) == pdTRUE){
            portENTER_CRITICAL();
            {
              handleRx(xRxedData);
            
            }
            portEXIT_CRITICAL();
            
           }
           
      }
} 
/*-----------------------------------------------------------*/
/*-----------------------------------------------------------*/
/*                        TRANSFER                           */
/*-----------------------------------------------------------*/
static portTASK_FUNCTION( vTransmitAll, pvParameters ) // transfer anything on the tx queue
{
  int i = 0;
  char s='H';
  PACKET pkt;
  portTickType xCurTime = xTaskGetTickCount();
  
  vTaskDelay(comSEND_DELAY*4);
  for(;;){
    vTaskDelay(comSEND_DELAY*2);
      //putChar('A');
                             // Packet address
      if((xTaskGetTickCount() - xCurTime) >= comHEART_SEND){
      if(currentMode == 1){
        createHeartbeatPacket(&pkt, (char)beatCount);
        //createReplyPacket(&pkt, '4', '0');
      } else {
        createHostbeaconPacket(&pkt);
      }
        if( xSemaphoreTake( xTxSemaphore, portMAX_DELAY ) == pdTRUE ) //place get semaphore and place Heartbeat/Hostbeacon on the queue
        { 
          xSerialSendPkt( xPort, &pkt, comSEND_DELAY*2, 1 );

          xSemaphoreGive( xTxSemaphore );
        }
        xCurTime = xTaskGetTickCount();
        beatCount++;
        if(beatCount >= 4){
          //endGame
        }
      }

      if( (xSerialGetToSend( xPort, &pkt, comSEND_DELAY*4 )) ){
                //println(txBuffer+4);
                portENTER_CRITICAL();
                { 
                  sendPacket(&pkt);
                  //RFSendPacket(txBuffer, pkt.length+1);            // Send value over RF
                  
                }
                portEXIT_CRITICAL();
                ccCommandStrobe(SRX);
                //while(!(TI_CC_SPIReadStatus(MARCSTATE) == SIDLE));
                
                //RFSendPacket(&s, 2);            // Send value over RF
                P2IFG &= ~0x10;
                
       
      }
  }
}//end ccRX


/*-----------------------------------------------------------*/
